#include "electronicscaletool.h"
#include "excelengine.h"
#include <QDateTime>

#define TIME_TYPE_DATETIME      0 // 时间使用QDateTime
#define TIME_TYPE_STRING        1 // 时间使用字符串
#define TIME_TYPE_TIME          2 // 时间使用QTime

#define TIME_TYPE               TIME_TYPE_TIME

ElectronicScaleTool::ElectronicScaleTool(QObject *parent) : QObject(parent)
{

}

QString ElectronicScaleTool::toString(electronicscale::WeightUnit unit)
{
    if (unit == electronicscale::WeightUnit_g)
        return QString("g");
    else if (unit == electronicscale::WeightUnit_ct)
        return QString("ct");
    else
        return QString();
}

QString ElectronicScaleTool::toString(electronicscale::WeightRateUnit unit)
{
    if (unit == electronicscale::WeightRateUnit_g_h)
        return QString("g/h");
    else if (unit == electronicscale::WeightRateUnit_ct_h)
        return QString("ct/h");
    else
        return QString();
}

electronicscale::WeightUnit ElectronicScaleTool::toWeightUnit(const QString &unit)
{
    if (unit == "g")
        return electronicscale::WeightUnit_g;
    else if (unit == "ct")
        return electronicscale::WeightUnit_ct;
    else
        return electronicscale::_WeightUnit_Max;
}

electronicscale::WeightRateUnit ElectronicScaleTool::toWeightRateUnit(const QString &unit)
{
    if (unit == "g/h")
        return electronicscale::WeightRateUnit_g_h;
    else if (unit == "ct")
        return electronicscale::WeightRateUnit_ct_h;
    else
        return electronicscale::_WeightRateUnit_Max;
}


electronicscale::WeightRateUnit ElectronicScaleTool::getMatchup(const electronicscale::WeightUnit &weightUnit)
{
    if (weightUnit == electronicscale::WeightUnit_g)
        return electronicscale::WeightRateUnit_g_h;
    else if (weightUnit == electronicscale::WeightUnit_ct)
        return electronicscale::WeightRateUnit_ct_h;
    else
        return electronicscale::_WeightRateUnit_Max;
}

electronicscale::WeightUnit ElectronicScaleTool::getMatchup(const electronicscale::WeightRateUnit &weightRateUnit)
{
    if (weightRateUnit == electronicscale::WeightRateUnit_g_h)
        return electronicscale::WeightUnit_g;
    else if (weightRateUnit == electronicscale::WeightRateUnit_ct_h)
        return electronicscale::WeightUnit_ct;
    else
        return electronicscale::_WeightUnit_Max;
}

electronicscale::Sample ElectronicScaleTool::getLinearWeight(qint64 time, const electronicscale::Sample &start, const electronicscale::Sample &stop)
{
    electronicscale::Sample sample;

    sample.time = time;
    sample.weight.unit = start.weight.unit;

    // 线性插值
    double k = (stop.weight.value - start.weight.value)
            / (stop.time - start.time);
    sample.weight.value = k * (time - start.time) + start.weight.value;

    return sample;
}

electronicscale::Sample ElectronicScaleTool::findWeight(qint64 time, const QList<electronicscale::Sample> &samples)
{
    electronicscale::Sample sample;
    if (samples.size() == 0)
    {
        sample.time = 0;
        return sample;
    }
    if (samples.size() == 1)
    {
        sample = samples.at(0);
        return sample;
    }
    int startIndex = 0;
    int stopIndex = samples.size() - 1;
    int tmpIndex;
    bool isFound = false;
    bool isInMiddle = false;
    int state = 0;
    while(!isFound)
    {
        switch (state)
        {
            case 0: // 检查最小值
                if (samples.at(startIndex).time < time)
                {
                    state = 1;
                }
                else
                {
                    if (samples.at(startIndex).time == time)
                    {
                        isFound = true;
                        tmpIndex = startIndex;
                    }
                    else
                    {
                        // 看做不变
                        isFound = true;
                        tmpIndex = startIndex;
                    }
                }
                break;
            case 1: // 检查最大值
                if (samples.at(stopIndex).time > time)
                {
                    state = 2;
                }
                else
                {
                    if (samples.at(stopIndex).time == time)
                    {
                        isFound = true;
                        tmpIndex = stopIndex;
                    }
                    else
                    {
                        // 看做不变
                        isFound = true;
                        tmpIndex = stopIndex;
                    }
                }
                break;
            case 2: // 二分查找
                tmpIndex = (startIndex + stopIndex) / 2;
                if (samples.at(tmpIndex).time == time)
                {
                    isFound = true;
                }
                else if (samples.at(tmpIndex).time > time) // 在上半段
                {
                    stopIndex = tmpIndex;
                }
                else // 在下半段
                {
                    startIndex = tmpIndex;
                }

                // 在2个连续的点中间，则直接返回这2个点再做进一步计算
                if (stopIndex - startIndex <= 1)
                {
                    isFound = true;
                    isInMiddle = true;
                }
                break;
            default:
                state = 0;
                break;
        }
    }

    if (isInMiddle)
    {
        sample = getLinearWeight(time, samples.at(startIndex), samples.at(stopIndex));
    }
    else
    {
        sample = samples.at(tmpIndex);
    }
    return sample;
}

int ElectronicScaleTool::findIndex(qint64 time, const QList<electronicscale::Sample> &samples)
{
    if (samples.size() == 0)
        return -1;
    if (samples.size() == 1)
        return 0;

    int startIndex = 0;
    int stopIndex = samples.size() - 1;
    int tmpIndex;
    bool isFound = false;
    bool isInMiddle = false;
    int state = 0;
    while(!isFound)
    {
        switch (state)
        {
            case 0: // 检查最小值
                if (samples.at(startIndex).time < time)
                {
                    state = 1;
                }
                else
                {
                    if (samples.at(startIndex).time == time)
                    {
                        isFound = true;
                        tmpIndex = startIndex;
                    }
                    else
                    {
                        // 看做不变
                        isFound = true;
                        tmpIndex = startIndex;
                    }
                }
                break;
            case 1: // 检查最大值
                if (samples.at(stopIndex).time > time)
                {
                    state = 2;
                }
                else
                {
                    if (samples.at(stopIndex).time == time)
                    {
                        isFound = true;
                        tmpIndex = stopIndex;
                    }
                    else
                    {
                        // 看做不变
                        isFound = true;
                        tmpIndex = stopIndex;
                    }
                }
                break;
            case 2: // 二分查找
                tmpIndex = (startIndex + stopIndex) / 2;
                if (samples.at(tmpIndex).time == time)
                {
                    isFound = true;
                }
                else if (samples.at(tmpIndex).time > time) // 在上半段
                {
                    stopIndex = tmpIndex;
                }
                else // 在下半段
                {
                    startIndex = tmpIndex;
                }

                // 在2个连续的点中间，则直接返回这2个点再做进一步计算
                if (stopIndex - startIndex <= 1)
                {
                    isFound = true;
                    isInMiddle = true;
                }
                break;
            default:
                state = 0;
                break;
        }
    }

    if (isInMiddle)
    {
        return startIndex;
    }
    else
    {
        return tmpIndex;
    }
}

bool ElectronicScaleTool::exportWeightDataToXlsx(const QString &path, const QList<electronicscale::Sample> &samples)
{
    if (path.isEmpty())
        return false;

    bool ret = false;

    ExcelEngine excel;
    ret = excel.OpenSheet(path, "Weight");
    if (!ret)
        return false;

    QVariant var;
    var.setValue(QString("time"));
    excel.SetCellData(1, 1, var);
    var.setValue(QString("weight"));
    excel.SetCellData(1, 2, var);
    var.setValue(QString("unit"));
    excel.SetCellData(1, 3, var);

    int row = 2;
    foreach (auto sample, samples)
    {
        QVariant var;
#if (TIME_TYPE == TIME_TYPE_DATETIME)
        var.setValue(QDateTime::fromMSecsSinceEpoch(sample.time).toString("yyyy-MM-dd hh:mm:ss.zzz"));
#elif (TIME_TYPE == TIME_TYPE_STRING)
        var.setValue(QDateTime::fromMSecsSinceEpoch(sample.time).toString("yyyy_MM_dd__hh_mm_ss_zzz"));
#elif (TIME_TYPE == TIME_TYPE_TIME)
        var.setValue(QDateTime::fromMSecsSinceEpoch(sample.time).toString("hh:mm:ss"));
#endif
        excel.SetCellData(row, 1, var);
        var.clear();
        var.setValue(sample.weight.value);
        excel.SetCellData(row, 2, var);
        QString unit;
        unit = toString(sample.weight.unit);
        var.clear();
        var.setValue(unit);
        excel.SetCellData(row, 3, var);
        row++;
    }
    excel.Save();
    excel.Close();

    return true;
}

bool ElectronicScaleTool::importWeightDataFromXlsx(const QString &path, QList<electronicscale::Sample> &samples)
{
    if (path.isEmpty())
        return false;

    bool ret = false;

    ExcelEngine excel;
    ret = excel.OpenSheet(path, "Weight");
    if (!ret)
        return false;
    int row = excel.GetRowCount();
    QVariant var;
    electronicscale::Sample sample;

#if (TIME_TYPE == TIME_TYPE_TIME)
    QTime prevTime, time;
    QDate date = QDate::currentDate();
#endif
    for (int r = 2; r <= row; r++) // 从第二行开始，因为第一行是表头
    {
        var = excel.GetCellData(r, 1);
#if (TIME_TYPE == TIME_TYPE_DATETIME)
        if (var.canConvert<QDateTime>())
        {
            QDateTime t = var.value<QDateTime>();
            sample.time = t.toMSecsSinceEpoch();
        }
        else
        {
            continue;
        }
#elif (TIME_TYPE == TIME_TYPE_STRING)
        if (var.canConvert<QString>())
        {
            QString str = var.value<QString>();
            QStringList list = str.split('_');
            if (list.size() != 8)
                continue;
            QDateTime t;
            QDate date;
            date.setDate(list.at(0).toInt(), list.at(1).toInt(), list.at(2).toInt());
            QTime time;
            time.setHMS(list.at(4).toInt(), list.at(5).toInt(), list.at(6).toInt(), list.at(7).toInt());
            t.setDate(date);
            t.setTime(time);
            sample.time = t.toMSecsSinceEpoch();
        }
        else
        {
            continue;
        }
#elif (TIME_TYPE == TIME_TYPE_TIME)
        if (var.canConvert<QTime>())
        {
            time = var.value<QTime>();
            if (r == 2)
            {
                prevTime = time;
            }
            else
            {
                if (time < prevTime)
                {
                    date = date.addDays(1);
                }
                prevTime = time;
            }

            QDateTime t;
            t.setDate(date);
            t.setTime(time);
            sample.time = t.toMSecsSinceEpoch();
        }
        else
        {
            continue;
        }
#endif

        var = excel.GetCellData(r, 2);
        if (var.canConvert<double>())
        {
            sample.weight.value = var.toDouble();
        }
        else
        {
            continue;
        }

        var = excel.GetCellData(r, 3);
        if (var.canConvert<QString>())
        {
            QString unit = var.toString();
            sample.weight.unit = toWeightUnit(unit);
        }
        else
        {
            continue;
        }

        samples << sample;
    }
    excel.Close();

    return true;
}

bool ElectronicScaleTool::exportIntervalRateToXlsx(const QString &path, const QList<electronicscale::IntervalRate> &rates)
{
    if (path.isEmpty())
        return false;

    bool ret = false;

    ExcelEngine excel;
    ret = excel.OpenSheet(path, "Interval Rate", QIODevice::Truncate);
    if (!ret)
        return false;

    // 设置表头
    QVariant var;
    var.setValue(QString("time"));
    excel.SetCellData(1, 1, var);
    var.setValue(QString("current weight"));
    excel.SetCellData(1, 2, var);
    var.setValue(QString("rate"));
    excel.SetCellData(1, 3, var);
    var.setValue(QString("rate unit"));
    excel.SetCellData(1, 4, var);

    int row = 2;
    foreach (auto rate, rates)
    {
        QVariant var;
//        var.setValue(QDateTime::fromMSecsSinceEpoch(rate.time).toString("yyyy-MM-dd hh:mm:ss.zzz"));
        var.setValue(QDateTime::fromMSecsSinceEpoch(rate.time).toString("yyyy_MM_dd__hh_mm_ss_zzz"));
        excel.SetCellData(row, 1, var);
        var.clear();
        var.setValue(rate.rate.currentWeight);
        excel.SetCellData(row, 2, var);
        var.clear();
        var.setValue(rate.rate.value);
        excel.SetCellData(row, 3, var);
        QString unit;
        unit = toString(rate.rate.unit);
        var.clear();
        var.setValue(unit);
        excel.SetCellData(row, 4, var);
        row++;
    }
    excel.Save();
    excel.Close();

    return true;
}

bool ElectronicScaleTool::importIntervalRateFromXlsx(const QString &path, QList<electronicscale::IntervalRate> &rates)
{
    if (path.isEmpty())
        return false;

    bool ret = false;

    ExcelEngine excel;
    ret = excel.OpenSheet(path, "Interval Rate");
    if (!ret)
        return false;
    int row = excel.GetRowCount();
    QVariant var;
    electronicscale::IntervalRate rate;
    for (int r = 2; r <= row; r++) // 从第二行开始，因为第一行是表头
    {
        var = excel.GetCellData(r, 1);
//        if (var.canConvert<QDateTime>())
//        {
//            QDateTime t = var.value<QDateTime>();
//            rate.time = t.toMSecsSinceEpoch();
//        }
//        else
//        {
//            continue;
//        }
        if (var.canConvert<QString>())
        {
            QString str = var.value<QString>();
            QStringList list = str.split('_');
            if (list.size() != 8)
                continue;
            QDateTime t;
            QDate date;
            date.setDate(list.at(0).toInt(), list.at(1).toInt(), list.at(2).toInt());
            QTime time;
            time.setHMS(list.at(4).toInt(), list.at(5).toInt(), list.at(6).toInt(), list.at(7).toInt());
            t.setDate(date);
            t.setTime(time);
            rate.time = t.toMSecsSinceEpoch();
        }
        else
        {
            continue;
        }

        var = excel.GetCellData(r, 2);
        if (var.canConvert<double>())
        {
            rate.rate.currentWeight = var.toDouble();
        }
        else
        {
            continue;
        }

        var = excel.GetCellData(r, 3);
        if (var.canConvert<double>())
        {
            rate.rate.value = var.toDouble();
        }
        else
        {
            continue;
        }

        var = excel.GetCellData(r, 4);
        if (var.canConvert<QString>())
        {
            QString unit = var.toString();
            rate.rate.unit = toWeightRateUnit(unit);
        }
        else
        {
            continue;
        }

        rates << rate;
    }
    excel.Close();

    return true;
}
